import Vue from "vue";

declare global {
    export interface Element {
        appearAction?: () => void
        disappearAction?: () => void
        bindingObserver?: IntersectionObserver
        lazyRootObserver?: IntersectionObserver
    }
}

const globalObserver = new IntersectionObserver((entries) => {
    for (const entry of entries) {
        if (entry.isIntersecting) {
            entry.target.appearAction?.()
        } else {
            entry.target.disappearAction?.()
        }
    }
})


Vue.directive('lazy-root', {
    bind: (el, binding) => {
        el.lazyRootObserver = new IntersectionObserver((entries) => {
            for (const entry of entries) {
                if (entry.isIntersecting) {
                    entry.target.appearAction?.()
                } else {
                    entry.target.disappearAction?.()
                }
            }
        }, {
            root: el,
            threshold: 0.5
        })
    },
    unbind: (el, binding) => {
        el.lazyRootObserver?.disconnect()
        delete el.lazyRootObserver
    }
})

function getLazyRootObserver(el: Element) {
    let cur = el.parentElement
    while (cur) {
        if (cur.lazyRootObserver) {
            return cur.lazyRootObserver
        } else {
            cur = cur.parentElement
        }
    }
}

function updateObserver(el: Element) {
    const ob = getLazyRootObserver(el)
    el.bindingObserver?.unobserve(el)
    el.bindingObserver = ob
    if (ob) {
        ob.observe(el)
    }
}

Vue.directive('appear', {
    inserted: (el, binding) => {
        el.appearAction = binding.value
        updateObserver(el)
    },
    update: (el, binding) => {
        el.appearAction = binding.value
        updateObserver(el)
    },
    unbind: (el, binding) => {
        el.appearAction = undefined
        el.bindingObserver?.unobserve(el)
    }
})

Vue.directive('disappear', {
    inserted: (el, binding) => {
        el.disappearAction = binding.value
        updateObserver(el)
    },
    update: (el, binding) => {
        el.disappearAction = binding.value
        updateObserver(el)
    },
    unbind: (el, binding) => {
        el.disappearAction = undefined
        el.bindingObserver?.unobserve(el)
    }
})